package org.leon.mvcframework.servlet;

import org.leon.mvcframework.BaseInterceptor;
import org.leon.mvcframework.annotations.*;
import org.leon.mvcframework.pojo.Handler;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Created by leon.L on 2020/1/22.
 * Desc : 自定义 MVC 的分发 Servlet
 */
public class MyDispatchServlet extends HttpServlet {

    private Properties properties = new Properties();

    private List<String> classNameList = new ArrayList<>();

    // ioc 容器
    private Map<String, Object> iocMap = new HashMap<>();

    // handlerMapping 映射关系
    private List<Handler> handlerList = new ArrayList<>();

    // 拦截器列表
    private List<BaseInterceptor> interceptors = new ArrayList<>();

    @Override
    public void init(ServletConfig config) throws ServletException {

        // 1. 加载配置文件
        String contextConfigLocation = config.getInitParameter("contextConfigLocation");
        doLoadConfig(contextConfigLocation);

        // 2. 扫描相关类，基于注解
        doScanPackage(properties.getProperty("scan.package"));

        // 3. 初始化 bean 对象（Ioc 容器，基于注解）
        doInstance();

        // 4. 实现基于 @Autowired 的依赖注入
        doDependence();

        // 5. 构造一个 MappingHandler 映射器，维护 url 和 handler 的对应关系
        initMappingHandler();

        // 6. 绑定拦截器
        bindInterceptor();

        System.out.println("MVC init successfully.");
    }

    /**
     * 加载资源配置文件
     * @param contextConfigLocation
     */
    private void doLoadConfig(String contextConfigLocation) {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
        try {
            properties.load(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 构造一个 MappingHandler 映射器，维护 url 和 handler 的对应关系
     */
    private void initMappingHandler() {
        if (iocMap.size() == 0) {
            return;
        }

        for (Map.Entry entry : iocMap.entrySet()) {
            // 只处理 controller
            if (entry.getValue().getClass().isAnnotationPresent(MvcController.class)) {

                String url = "";

                // 判断 controller 上有没有定义 RequestMapping
                if (entry.getValue().getClass().isAnnotationPresent(MvcRequestMapping.class)) {
                    MvcRequestMapping mappingAnnotation = entry.getValue().getClass().getAnnotation(MvcRequestMapping.class);
                    url = mappingAnnotation.value().trim();
                }

                // 遍历所有带有 RequestMapping 注解的方法，拼接 url
                Method[] methods = entry.getValue().getClass().getMethods();
                for (Method method : methods) {
                    if (method.isAnnotationPresent(MvcRequestMapping.class)) {
                        String methodUrl = method.getAnnotation(MvcRequestMapping.class).value();
                        url += methodUrl;

                        Handler handler = new Handler(Pattern.compile(url), method, entry.getValue());
                        Parameter[] parameters = method.getParameters();

                        for (int i = 0; i < parameters.length; i++) {
                            Parameter parameter = parameters[i];
                            // 如果方法参数有 HttpServletRequest 和 HttpServletResponse 则取类名，放入位置对应关系
                            if (parameter.getType() == HttpServletRequest.class || parameter.getType() == HttpServletResponse.class) {
                                handler.getParamMap().put(parameter.getType().getSimpleName(), i);
                            } else {
                                handler.getParamMap().put(parameter.getName(), i);
                            }
                        }
                        // 给 handler 绑定拦截器
                        handler.getInterceptors().addAll(interceptors);
                        handlerList.add(handler);
                    }
                }
            }
        }
    }

    /**
     * 实现基于 @Autowired 的依赖注入
     */
    private void doDependence() {
        if (iocMap.size() == 0) {
            return;
        }

        for (Map.Entry entry : iocMap.entrySet()) {
            Field[] declaredFields = entry.getValue().getClass().getDeclaredFields();
            for (Field field : declaredFields) {
                if (field.isAnnotationPresent(MvcAutowired.class)) {
                    MvcAutowired autowiredAnno = field.getAnnotation(MvcAutowired.class);
                    String beanName = autowiredAnno.value();
                    if (beanName.trim() .equals("")) {
                        beanName = field.getType().getName();
                    }

                    field.setAccessible(true);
                    try {
                        field.set(entry.getValue(), iocMap.get(beanName));
                    }catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     * 初始化 bean 对象（Ioc 容器，基于注解）
     */
    private void doInstance() {
        if (classNameList.size() == 0) {
            return;
        }

        try {
            for (String className : classNameList) {
                Class<?> aClass = Class.forName(className);

                // 如果是个接口，直接跳过
                if (aClass.isInterface()) {
                    continue;
                }

                // 获取默认空构造函数，通过空构造函数实例化
                Constructor<?> defaultConstructor = aClass.getDeclaredConstructor();
                Object o = defaultConstructor.newInstance();

                if (aClass.isAnnotationPresent(MvcController.class)) {
                    String beanName = firstCharacter(aClass.getSimpleName());
                    iocMap.put(beanName, o);
                } else if (aClass.isAnnotationPresent(MvcService.class)) {
                    MvcService serviceAnnotation = aClass.getAnnotation(MvcService.class);
                    String beanName = serviceAnnotation.value();

                    // 判断是否设置来 service 的名称
                    if (beanName.trim() == "") {
                        beanName = firstCharacter(aClass.getSimpleName());
                    }
                    iocMap.put(beanName, o);

                    // 获取当前 service 所有的实现接口，然后把所有接口的全限定名称指向当前 service 实例
                    Class<?>[] interfaces = aClass.getInterfaces();
                    for (Class<?> anInterface : interfaces) {
                        iocMap.put(anInterface.getName(), o);
                    }
                } else if (aClass.isAnnotationPresent(MvcInterceptor.class)) {
                    // 把拦截器存到列表中
                    Class<?>[] interfaces = aClass.getInterfaces();
                    // 是否直接继承了 BaseInterceptor
                    if (interfaces.length > 0) {
                        Set<Class<?>> classSet = Stream.of(interfaces).collect(Collectors.toSet());
                        if (classSet.contains(BaseInterceptor.class)) {
                            interceptors.add((BaseInterceptor) o);
                        }
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 扫描指定包以及子包，找到基于注解的类：@MvcService  @MvcController
     * @param rootPackage 指定根包
     */
    private void doScanPackage(String rootPackage) {
        String filePath = Thread.currentThread().getContextClassLoader().getResource("").getPath()
                + rootPackage.replaceAll("\\.", "/");

        File[] files = new File(filePath).listFiles();
        for (File f : files) {
            if (f.isDirectory()) {
                // 递归遍历
                doScanPackage(rootPackage + "." + f.getName());
            } else if (f.getName().endsWith(".class")){
                String className = rootPackage + "." + f.getName().replaceAll(".class", "");
                classNameList.add(className);
            }
        }
    }

    /**
     * 绑定拦截器
     */
    private void bindInterceptor() {
        if (iocMap.size() == 0) {
            return;
        }

        for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
            if (entry.getValue().getClass().isAnnotationPresent(MvcController.class)) {

            }
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String uri = req.getRequestURI();
        Handler handler = getHandler(uri);

        // 处理拦截器链
        List<BaseInterceptor> interceptors = handler.getInterceptors();
        for (BaseInterceptor interceptor : interceptors) {
            if (! interceptor.beforeHandler(req, resp, handler)) {
                return;
            }
        }

        if (handler == null) {
            resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
            resp.getWriter().write("404");
            return;
        }

        // 获取当前 handler 中参数的名称及顺序
        Map<String, Integer> paramMap = handler.getParamMap();
        // 初始化实际调用时所用的参数数组
        Object[] args = new Object[paramMap.size()];

        // 获取请求中所有的参数
        Map<String, String[]> parameterMap = req.getParameterMap();

        // 遍历请求参数，把对应名称的值放到参数数组中
        for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
            String[] values = entry.getValue();
            String value = String.join(",", values);

            if (paramMap.keySet().contains(entry.getKey())) {
                int idx = paramMap.get(entry.getKey());
                args[idx] = value;
            }
        }

        // 判断 handler 方法中是否有 HttpServletRequest 和 HttpServletResponse
        if (paramMap.containsKey(HttpServletRequest.class.getSimpleName())) {
            int idx = paramMap.get(HttpServletRequest.class.getSimpleName());
            args[idx] = req;
        }
        if (paramMap.containsKey(HttpServletResponse.class.getSimpleName())) {
            int idx = paramMap.get(HttpServletResponse.class.getSimpleName());
            args[idx] = resp;
        }

        try {
            handler.getMethod().invoke(handler.getController(), args);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据 uri 查找对应的 handler
     * @param uri
     * @return
     */
    private Handler getHandler(String uri){
        if (handlerList.size() == 0) {
            return null;
        }

        for (Handler handler : handlerList) {
            Matcher matcher = handler.getPattern().matcher(uri);
            if (matcher.matches()) {
                return handler;
            }
        }
        return null;
    }


    /**
     * 首字母小写
     */
    private String firstCharacter (String str) {
        char[] chars = str.toCharArray();
        if ( chars[0] >= 'A' && chars[0] <= 'Z') {
            chars[0] += 32;
        }
        return String.valueOf(chars);
    }
}
